#include <stdio.h>
#include <pthread.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>

#define ERROR_CHECK(ret, funcName) \
do{ \
    if(ret != 0) \
    { \
        printf("%s : %s \n", funcName, strerror(ret));\
        exit(1); \
    } \
}while(0);

//主线程唤醒子线程，子线程只有被唤醒的时候，才能执行打印操作
//主线程也需要进行打印(将打印看成是共享资源)
//简化了这个操作：
//子线程在打印的时候，直接就是一个简单printf
//主线程在打印的时候，直接就是一个简单printf

typedef struct
{
    int num;
    pthread_mutex_t mutex;//互斥锁
    pthread_cond_t cond;//条件变量
}Data_t;

void *threadFunc(void *arg)
{
    Data_t *pdata = (Data_t *)arg;//就是子线程从main中传过来的

    printf("begin child thread\n");

    int ret = pthread_mutex_lock(&pdata->mutex);
    ERROR_CHECK(ret, "pthread_mutex_lock child");

    printf("child thread lock\n");

    //会分为两个部分
    //1、上半部：(在条件变量上)排队，解锁，睡眠
    //2、下半部：(从条件变量上)被唤醒, 上锁，函数返回
    ret = pthread_cond_wait(&pdata->cond, &pdata->mutex);
    ERROR_CHECK(ret, "pthread_cond_wait child");

    //共享资源
    printf("I'm child thread.I'm waking up\n");

    ret = pthread_mutex_unlock(&pdata->mutex);
    ERROR_CHECK(ret, "pthread_mutex_unlock child");

    printf("finish child thread\n");

    //子线程主动退出函数
    pthread_exit(NULL);
}

int main(int argc, char *argv[])
{
    Data_t data;
    data.num = 10;

    //初始化互斥锁
    int ret = pthread_mutex_init(&data.mutex, NULL);
    ERROR_CHECK(ret, "pthread_mutex_init");

    //初始化条件变量
    ret = pthread_cond_init(&data.cond, NULL);
    ERROR_CHECK(ret, "pthread_cond_init");

    pthread_t thid;//子线程id
    ret = pthread_create(&thid, NULL, threadFunc, &data);
    ERROR_CHECK(ret, "pthread_create");

    //让主线程先睡眠，将CPU的控制权交给子线程
    usleep(100);

    //肯定要在子线程创建之后
    ret = pthread_mutex_lock(&data.mutex);
    ERROR_CHECK(ret, "pthread_mutex_lock main");

    //主线程发唤醒子线程去执行任务，也就是打印
    ret = pthread_cond_signal(&data.cond);
    ERROR_CHECK(ret, "pthread_cond_signal main");

    //共享资源
    printf("I'm main thread.I'm sending signal\n");

    ret = pthread_mutex_unlock(&data.mutex);
    ERROR_CHECK(ret, "pthread_mutex_unlock main");

    printf("main thread unlock\n");

    //主线程等待子线程的回收
    ret = pthread_join(thid, NULL);
    ERROR_CHECK(ret, "pthread_join");

    //互斥锁销毁
    ret = pthread_mutex_destroy(&data.mutex);
    ERROR_CHECK(ret, "pthread_mutex_destroy");

    //条件变量销毁
    ret = pthread_cond_destroy(&data.cond);
    ERROR_CHECK(ret, "pthread_cond_destroy");

    return 0;
}

